﻿using System;
using UnityEngine;

public static class Noise {
    public enum NormalizeMode { Local, Global }

    #region 自定义函数
    /// <summary>
    /// 生成噪声图
    /// </summary>
    /// <param name="mapWidth">图的宽度</param>
    /// <param name="mapHeight">图的高度</param>
    /// <param name="scale">缩放</param>
    /// <param name="seed">种子</param>
    /// <param name="offset">偏移</param>
    /// <param name="normalizeMode">单位化噪声的方式</param>
    /// <returns>噪声图</returns>
    public static float[, ] GenerateNoiseMap (int mapWidth, int mapHeight, int seed, float scale, int octaves, float persistance, float lacunarity, Vector2 offset, NormalizeMode normalizeMode) {
        // 随机种子
        // 关于Random的API     https://docs.microsoft.com/zh-cn/dotnet/api/system.random?redirectedfrom=MSDN&view=netframework-4.8#Instantiate
        // 相同的随机种子会生成一系列相同的随机数
        System.Random prng = new System.Random (seed);
        Vector2[] octaveOffSets = new Vector2[octaves];

        float maxPossibleHeight = 0; // 可能的最高高度
        float amplitude = 1; // 振幅
        float frequency = 1; // 频率

        for (int i = 0; i < octaves; i++) {
            float offsetX = prng.Next (-10000, 10000) + offset.x;
            float offsetY = prng.Next (-10000, 10000) - offset.y; // - offset.y 用于反转偏移的方向
            octaveOffSets[i] = new Vector2 (offsetX, offsetY);

            maxPossibleHeight += amplitude;
            amplitude *= persistance;
        }

        // 噪声图
        float[, ] noiseMap = new float[mapWidth, mapHeight];
        // 排除scale不符合要求的情况
        if (scale <= 0) scale = 0.0001f;
        float maxLocalNoiseHeight = float.MinValue;
        float minLocalNoiseHeight = float.MaxValue;

        // 让噪声在中间生成
        float halfWidth = mapWidth / 2f;
        float halfHeight = mapHeight / 2f;

        // 遍历所有像素
        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                amplitude = 1; // 振幅
                frequency = 1; // 频率
                float noiseHight = 0; // 噪声高度 

                // 这部分循环是在合成多层次的噪声 原理看教程第一个视频
                for (int i = 0; i < octaves; i++) {
                    // 取样的值
                    float sampleX = (x - halfWidth + octaveOffSets[i].x) / scale * frequency;
                    float sampleY = (y - halfHeight + octaveOffSets[i].y) / scale * frequency;
                    // 生成柏林噪音
                    float perlinValue = Mathf.PerlinNoise (sampleX, sampleY) * 2 - 1;
                    // 进行一些运算
                    noiseHight += perlinValue * amplitude;
                    amplitude *= persistance;
                    frequency *= lacunarity;
                }
                // 计算出噪声的范围
                if (noiseHight > maxLocalNoiseHeight) maxLocalNoiseHeight = noiseHight;
                else if (noiseHight < minLocalNoiseHeight) minLocalNoiseHeight = noiseHight;
                // 记录噪音信息
                noiseMap[x, y] = noiseHight; // 此时的noiseMap的值不一定在0-1之间
            }
        }

        // 遍历所有像素
        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                if (normalizeMode == NormalizeMode.Local) {
                    // 如果是使用local的方式单位化，用最大和最小插值，控制在0-1之间
                    noiseMap[x, y] = Mathf.InverseLerp (minLocalNoiseHeight, maxLocalNoiseHeight, noiseMap[x, y]);
                } else {
                    // 如果是用global的方法单位化，把数值按照前面的函数相反计算回去（*2-1变成（+1）/2,后来又加一个*2和2抵消掉了）
                    float normalizedHeight = (noiseMap[x, y] + 1) / (maxPossibleHeight);
                    // 让值大于0
                    noiseMap[x, y] = Mathf.Clamp (normalizedHeight, 0, int.MaxValue);
                }
            }
        }
        // 返回噪声图
        return noiseMap;
    }
    #endregion
}